package main

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/disintegration/imaging"
	"image"
	"image/jpeg"
	"log"
	"mms"
	"net/http"
	"os"
	"strconv"
	"strings"
)

var conf Config

type Config struct {
	ListenAddr string
	SrcPath    string
	CachePath  string
	RootDir    string
}

func main() {
	log.Println("Go Image Server")
	log.Println("start load config")
	loadConfig()
	log.Println("config loadded")

	handlePrefix := fmt.Sprintf("/%s/", strings.Trim(conf.RootDir, "/"))
	http.HandleFunc(handlePrefix, ImageHandler)
	http.HandleFunc("/", DefaultHandler)
	err := http.ListenAndServe(conf.ListenAddr, nil)
	if err != nil {
		log.Fatalln(err)
	}
	mms.Test()
}

func ImageHandler(w http.ResponseWriter, r *http.Request) {
	log.Println(r.URL)
	log.Println(r.RequestURI)
	log.Println(r.URL.Path)
	cacheFile := "404.jpg"

	//var validKeys []string = {"imageView2","w","h","q"}

	params := make(map[string]string)
	queryString := strings.Trim(r.URL.RawQuery, "/")
	if len(queryString) > 0 {
		querys := strings.Split(queryString, "/")
		for i := 0; i < len(querys); i++ {
			if (i + 1) >= len(querys) {
				break
			}
			params[querys[i]] = querys[i+1]
			i++
		}
	}

	srcPath := conf.RootDir + r.URL.Path
	if fileExists(srcPath) {
		cacheFile, _ = getCachedFile(srcPath, params)
	}

	http.ServeFile(w, r, cacheFile)
}

func getCachedFile(path string, params map[string]string) (string, error) {
	scaleType := 0
	w := 0
	h := 0
	q := 100

	if iv, ok := params["imageView2"]; ok {
		scaleType, _ = strconv.Atoi(iv)
	}
	if wv, ok := params["w"]; ok {
		w, _ = strconv.Atoi(wv)
	}
	if hv, ok := params["h"]; ok {
		h, _ = strconv.Atoi(hv)
	}
	if qv, ok := params["qv"]; ok {
		q, _ = strconv.Atoi(qv)
		if q < 10 || q > 100 {
			q = 100
		}
	}

	cacheKey := fmt.Sprintf("%s%s%s", path, w, h)
	hashKey := md5Hash(cacheKey)

	cachePath := conf.CachePath + "/" + hashKey + ".jpg"
	if fileExists(cachePath) {
		return cachePath, nil
	}

	file, err := os.Open(path)
	if err != nil {
		log.Println(err)
		return "", nil
	}
	defer file.Close()

	srcImg, _, err := image.Decode(file)
	if err != nil {
		log.Println(err)
		return "", nil
	}

	resizedImg, err := resizeAndSave(srcImg, scaleType, w, h)

	f, err := os.OpenFile(cachePath, os.O_RDWR|os.O_CREATE, 0777)
	defer f.Close()

	jpeg.Encode(f, resizedImg, &jpeg.Options{q})

	return cachePath, nil
}

func resizeAndSave(img image.Image, scaleType int, width int, height int) (image.Image, error) {
	return imaging.Resize(img, width, height, imaging.Lanczos), nil
}

func DefaultHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("<html><body><center><h1>It Works!</h1></center><hr><center>Image Go Server</center></body></html>"))
}

func md5Hash(plainText string) string {
	data := []byte(plainText)
	hash := md5.Sum(data)
	return fmt.Sprintf("%x", hash)
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

func loadConfig() {
	f, err := os.Open("config.json")
	if err != nil {
		log.Fatalln(err)
	}
	decoder := json.NewDecoder(f)
	err = decoder.Decode(&conf)

	if err != nil {
		log.Fatalln(err)
	}
}
